package com.hh.game.service.gateway.manager.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.task.Task;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.hh.game.service.gateway.manager.service.config.*;
import com.hh.game.service.gateway.pool.SingleThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;

@Component
public class ServiceConfigManager {
    private static Logger log = LoggerFactory.getLogger(ServiceConfigManager.class);

    private Map<String, Long> serviceConfigFileLastModifieds = new HashMap<>();

    private Map<ConfigType, LoadServiceConfig> loadConfigMap = new HashMap<>();

    private Map<String, Object> configCache = new ConcurrentHashMap<>();

    {
        loadConfigMap.put(ConfigType.JSON, new LoadServiceConfigForJson());
    }


    @Autowired
    private ServiceConfiguration config;
    /// 旧配置
    private ServiceConfiguration oldConfig;
    // 已加载的路由信息<配置的标识ID ,ServiceRouteVo>
    private Map<String, Set<ServiceRouteVo>> loadServiceRoutes = new ConcurrentHashMap<>();

    @PostConstruct
    public void loadService() throws Exception {
        if (config == null) {
            return;
        }

        oldConfig = config.clone();

        if (config.getDataIdSet() != null) {
            loadByConfigCenter();
        }
        if (config.getPath() != null) {
            loadServiceByLocal();
        }
        appServiceListener(config.getConfigID(), config.getConfigGroup());
    }

    /**
     * 监听配置[app.service]更新
     *
     * @param dataId
     * @param group
     */
    private void appServiceListener(String dataId, String group) {
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, config.getServerAddr());
            ConfigService configService = NacosFactory.createConfigService(properties);
            //注册监听
            configService.addListener(dataId, group, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String s) {
                    SingleThreadPool.get().schedule(() -> {
                        if (config == null) {
                            return;
                        }
                        try {
                            if (!config.equals(oldConfig)) {
                                Set<String> old = oldConfig.getDataIdSet() == null ? new HashSet<>() : oldConfig.getDataIdSet();
                                Set<String> newSet = config.getDataIdSet() == null ? new HashSet<>() : config.getDataIdSet();
                                old.stream().filter(id -> !newSet.contains(id))
                                        .forEach(id -> {
                                            Set<ServiceRouteVo> removes = loadServiceRoutes.get(id);
                                            removes.forEach(vo -> {
                                                ServiceRouteConfigCache.remove(vo.getService(),
                                                        ServiceRouteConfigCache.buildMethodSign(vo.getHandler(), vo.getMethod()));
                                            });
                                        });
                                loadByConfigCenter();
                                oldConfig = config.clone();
                            }
                        } catch (Exception e) {
                            log.error("retry load[app.service] config error", e);
                        }

                    }, 3, TimeUnit.SECONDS);
                }
            });
        } catch (Exception e) {
            log.error("listen service route config change error", e);
        }
    }

    /**
     * 加载配置中心配置
     */
    private void loadByConfigCenter() {
        try {
            Set<String> dataIdSet = config.getDataIdSet();
            String group = config.getGroup();

            for (String dataId : dataIdSet) {
                Properties properties = new Properties();
                properties.put(PropertyKeyConst.SERVER_ADDR, config.getServerAddr());
                ConfigService configService = NacosFactory.createConfigService(properties);
                //注册监听
                configService.addListener(dataId, group, new Listener() {
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        if (StrUtil.isBlank(configInfo)) {
                            ServiceRouteConfigCache.remove(dataId + ":" + group);
                        } else {
                            try {
                                ConfigResource resource = new DefaultConfigResource(dataId + ":" + group, configInfo);
                                loadResource(resource);
                            } catch (Exception e) {
                                log.error("load service route config by config center error[" + configInfo + "]", e.getMessage());
                            }
                        }
                    }

                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                });

                String content = configService.getConfig(dataId, group, 5000);
                ConfigResource resource = new DefaultConfigResource(dataId + ":" + group, content);
                loadResource(resource);
            }
        } catch (Exception e) {
            log.error("load service route config by config center error", e.getMessage());
        }
    }


    /**
     * 定时自动更新本地
     */
    private void updateServiceJob() {
        CronUtil.schedule("* */5 * * * *", new Task() {
            @Override
            public void execute() {
                try {
                    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                    Resource[] resources = resolver.getResources(config.getPath() + "*");
                    for (Resource resource : resources) {
                        ConfigResource r = new DefaultConfigResource(resource, new DefaultConfigResourceReader());
                        loadResource(r);
                    }
                } catch (Exception e) {
                    log.error("load service config error", e);
                }
            }
        });
    }

    /**
     * 加载本地service路由配置
     */
    public void loadServiceByLocal() {
        try {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources(config.getPath() + "*");
            for (Resource resource : resources) {
                ConfigResource r = new DefaultConfigResource(resource, new DefaultConfigResourceReader());
                loadResource(r);
            }
            updateServiceJob();
        } catch (Exception e) {
            log.error("load servinfig error", e);
        }
    }

    private void loadResource(ConfigResource resource) throws Exception {
        if(resource.isEmpty()){
            log.info("service route config[" + resource.getDataId() + "] is empty");
            return;
        }
        if (resource.isFile()) {
            File file = resource.getFile();
            String fileName = FileNameUtil.getName(file);
            serviceConfigFileLastModifieds.put(fileName, file.lastModified());
        }


        LoadServiceConfig load = loadConfigMap.get(resource.getType());
        if (load != null) {
            Set<ServiceRouteVo> result = load.load(resource, configCache.get(resource.getDataId()));
            configCache.put(resource.getDataId(), resource.getData());
            loadServiceRoutes.put(resource.getDataId(), result);
        } else {
            log.error("cannot load service config by type =>[" + resource.getType() + "]");
        }
    }

}
